#include "Actor.h"
//#include "TerrainDecal.h"

using namespace Ogre;

Actor::Actor()
{
	// Create our entity
	entity = sceneMgr->createEntity("Entity_"+name, meshName);
	entity->setMaterialName(matName);
	entity->setCastShadows(true);

	// Create our idle animation state, enable it
	idle = entity->getAnimationState(idleAni);
	idle->setLoop(true);
	idle->setEnabled(true);

	// Create our walk animation state, disable it
	walk = entity->getAnimationState(walkAni);
	walk->setLoop(true);
	walk->setEnabled(false);

	// Create our fight animation state, disable it
	fight = entity->getAnimationState(fightAni);
	fight->setLoop(true);
	fight->setEnabled(false);

	// Create our scene nodes and attach our entity
	baseNode = sceneMgr->getRootSceneNode()->createChildSceneNode("BaseNode_"+name, position);
	meshNode = baseNode->createChildSceneNode("MeshNode_"+name);
	meshNode->attachObject(entity);

	// Create our ray scene query and clamp our Actor to the terrain
	rayQuery = sceneMgr->createRayQuery(Ray(baseNode->getPosition(), Vector3::NEGATIVE_UNIT_Y));
	clampToTerrain();
}

Actor::~Actor()
{
	delete rayQuery;

	meshNode->detachAllObjects();
	sceneMgr->getRootSceneNode()->removeAndDestroyChild("BaseNode_"+name);
	sceneMgr->destroyEntity(entity);
}

void Actor::displayTitle(TitleCanvas* titleCanvas, const std::string& title)
{
	this->titleCanvas = titleCanvas;
	this->title = title;

}

void Actor::say(const std::string& message)
{
	/*if(titleCanvas)
	{
		titleCanvas->editTitle(entity, title + ": " + message);
		lastMessageTime = timer.getMilliseconds();
	}*/
}

void Actor::scale(Ogre::Real multiplier)
{
	meshNode->scale(multiplier, multiplier, multiplier);
	stepDistance *= multiplier;
}

Real Actor::facePoint(const Ogre::Vector3 &point)
{
	// Figure out the direction and distance to our target point from our position
	Vector3 direction = point - baseNode->getPosition();
	direction.y = 0;
	Real distance = direction.normalise();

	// Rotate our mesh towards our target point
	Vector3 meshOrient = meshNode->getOrientation() * Vector3::NEGATIVE_UNIT_Z;
	meshOrient.normalise();
	Quaternion quat = meshOrient.getRotationTo(direction);
	meshNode->rotate(quat);

	return distance;
}

void Actor::walkTo(const Ogre::Vector3 &point, Real durationSecs)
{
	if(!isWalking)
	{
		// Enable our walk animation state
		idle->setEnabled(false);
		walk->setEnabled(true);
	}

	isWalking = true;
	walkTarget = point;

	// Determine how fast we need to walk to get to the target by durationSecs
	Real distance = facePoint(walkTarget);
	walkSpeed = distance / durationSecs;
}

void Actor::update(Ogre::Real deltaTimeSecs)
{
	if(titleCanvas)
	{
		// If we have displayed the spoken message for more than displayMS
		if(timer.getMilliseconds() - lastMessageTime > displayMS && lastMessageTime)
		{
			// Reset our title
	/*		titleCanvas->editTitle(entity, title);
			lastMessageTime = 0;*/
		}
	}

	// If we're not walking, update the idle animation and return
	if(!isWalking)
	{
		idle->addTime(deltaTimeSecs);
		return;
	}

	// Update the walk animation, scaled to our walk-speed
	walk->addTime(deltaTimeSecs * (walkSpeed / stepDistance));

	// Figure out the direction and distance we need to walk until the target
	Vector3 direction = walkTarget - baseNode->getPosition();
	direction.y = 0;
	Real distance = direction.normalise() - (deltaTimeSecs * walkSpeed);

	// If we have reached our target
	if(distance <= 0.0f)
	{
		// Clamp our position to the target
		baseNode->setPosition(walkTarget);
		clampToTerrain();

		// Change our state to idle
		walk->setEnabled(false);
		idle->setEnabled(true);
		isWalking = false;

		return;
	}

	// Move our mesh towards the direction of the target, based on our walk speed and elapsed time
	baseNode->translate(direction * deltaTimeSecs * walkSpeed);
	clampToTerrain();
}

Ogre::SceneNode* Actor::getBaseNode()
{
	return baseNode;
}

std::string Actor::getName()
{
	return name;
}

std::string Actor::getTitle()
{
	return title;
}

void Actor::clampToTerrain()
{
	rayQuery->setRay(Ray(baseNode->getPosition() + Vector3(0, 200, 0), Vector3::NEGATIVE_UNIT_Y));
	RaySceneQueryResult &qryResult = rayQuery->execute();
	RaySceneQueryResult::iterator i = qryResult.begin();
	if(i != qryResult.end() && i->worldFragment)
		baseNode->setPosition(baseNode->getPosition().x, i->worldFragment->singleIntersection.y + 1, baseNode->getPosition().z);
}

